#include "socket_manager.h"
#include <unistd.h>
#include <iostream>
#include <thread>
#include <cstring>
#include <sstream>
#include "event2/event.h"
#include "event2/bufferevent.h"
#include "work_thread.h"
#include "com/simple_logger.h"
#include "com/file_utils.h"
#include "transmit/metadata.h"
#include "app_context.h"
#include "encrypt/crypto_graphic.h"
#include "encrypt/certification.h"
#include "com/utils.h"
#include "security_msg.pb.h"
#include "transmit/metadata.h"
#include "globe_type.h"

using namespace std;
using namespace sec;

// 读回调
static void SReadCb(struct bufferevent *buffev, void *ctx)
{
    SocketManager *manager = (SocketManager *)ctx;
    manager->ReadCb();
}

// 写回调
static void SWriteCb(struct bufferevent *buffev, void *ctx)
{
    SocketManager *manager = (SocketManager *)ctx;
    manager->WriteCb();
}

// 事件回调
static void SEventCb(struct bufferevent *buffev, short what, void *ctx)
{
    SocketManager *manager = (SocketManager *)ctx;
    manager->EventCb(what);
}

SocketManager::SocketManager(WorkThread *thread, event_base *base, int socket, int timeout)
    : ev_base_(base),
      socket_(socket),
      timeout_(timeout),
      work_thread_(thread)
{
}

SocketManager::~SocketManager()
{
    stringstream ss;
    ss << this->alias_ << " 断开连接！";
    LOG4CPLUS_DEBUG(SimpleLogger::Get()->LoggerRef(), ss.str());
    { // broadcast offline
        UserStatus user_status;
        UserStatus_State *state = user_status.add_status_list();
        state->set_alias(alias_);
        state->set_user_id(this->user_id_);
        state->set_is_on_line(false);
        MsgHeader status_header;
        status_header.set_msg_type(MsgType::USER_STATUS_NOTICE);
        status_header.set_from(this->user_id_);
        status_header.set_to(0);
        status_header.set_msg_size(user_status.ByteSizeLong());
        work_thread_->SendToNetBus(status_header.SerializeAsString().c_str(),
                                   status_header.ByteSizeLong(),
                                   user_status.SerializeAsString().c_str(),
                                   user_status.ByteSizeLong());
    }
    AppContext::Get()->UserOffLine(this->user_id_);
    bufferevent_free(buff_ev_);
    buff_ev_ = nullptr;
}

bool SocketManager::Init()
{
    // 创建bufferevent
    buff_ev_ = bufferevent_socket_new(ev_base_, socket_, BEV_OPT_CLOSE_ON_FREE);
    if (!buff_ev_)
    {
        return false;
    }
    // 指定参数
    bufferevent_setcb(buff_ev_, SReadCb, SWriteCb, SEventCb, this);
    bufferevent_enable(buff_ev_, EV_READ | EV_WRITE);
    timeval tv = {timeout_, 0};
    bufferevent_set_timeouts(buff_ev_, &tv, NULL);
    return true;
}

void SocketManager::ReadCb()
{
    char flag[5] = {0};
    int head_size = 0;
    // 判断报文头
    int len = bufferevent_read(buff_ev_, flag, 4);
    if (len <= 0 || strcmp(flag, PKT_FLAG) != 0)
    {
        return;
    }
    // 获得消息头大小
    len = bufferevent_read(buff_ev_, &head_size, 4);
    if (len <= 0)
    {
        return;
    }
    char *head = new char[head_size];
    len = bufferevent_read(buff_ev_, head, head_size);
    // 解析消息头对象
    MsgHeader msg_header;
    msg_header.ParseFromArray(head, head_size);
    delete[] head;
    char *msg_buff = new char[msg_header.msg_size()];
    // FIX:if msg_body too large
    len = bufferevent_read(buff_ev_, msg_buff, msg_header.msg_size());
    switch (msg_header.msg_type())
    {
    case MsgType::CONNECT_REQ:
        RecvConnectReqest(&msg_header, msg_buff, len);
        break;
    case MsgType::CLIENT_LOGIN_REQ: // 登录请求
        RecvLoginRequest(&msg_header, msg_buff, len);
        break;
    case MsgType::HEART_BEAT: // 心跳
        RecvHeartBeat(msg_buff, len);
        break;
    case MsgType::USER_INFO_REQ: // 用户信息请求
        if (msg_header.token() != this->token_)
        {
            LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), "用户通信令牌(token)验证错误！");
            return;
        }
        RecvUserInfoRequest(&msg_header, msg_buff, len);
        break;
    case MsgType::MSG_SEND_REQ: // 消息发送请求
    {
        if (msg_header.token() != this->token_)
        {
            LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), "用户通信令牌(token)验证错误！");
            return;
        }
        LOG4CPLUS_DEBUG(SimpleLogger::Get()->LoggerRef(), "MsgType::MSG_SEND_REQ");
        work_thread_->SendToNetBus(msg_header.SerializeAsString().c_str(), msg_header.ByteSizeLong(), msg_buff, msg_header.msg_size());
    }
    break;
    case MsgType::USER_STATUS_REQ: // 用户状态请求
        if (msg_header.token() != this->token_)
        {
            LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), "用户通信令牌(token)验证错误！");
            return;
        }
        LOG4CPLUS_DEBUG(SimpleLogger::Get()->LoggerRef(), "MsgType::USER_STATUS_REQ");
        RecvUserStatusRequest(&msg_header, msg_buff, len);
        break;
    case MsgType::SERVICE_REGIST: // 服务注册
        RecvServiceRegist(&msg_header, msg_buff, len);
        break;
    case MsgType::FILE_DOWNLOAD_NOTICE:
        work_thread_->SendToNetBus(msg_header.SerializeAsString().c_str(), msg_header.ByteSizeLong(), msg_buff, msg_header.msg_size());
        break;
    default:
    {
        stringstream ss;
        ss << "缺少对应的消息类型处理函数:" << msg_header.msg_type();
        LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), ss.str());
    }
    }
    delete[] msg_buff;
}

void SocketManager::WriteCb()
{
}

void SocketManager::EventCb(short event)
{
    if (event & BEV_EVENT_TIMEOUT)
    {
        work_thread_->Disconnect(this);
    }
    if (event & BEV_EVENT_EOF)
    {
        work_thread_->Disconnect(this);
    }
    // 用户断开连接，释放服务端资源
    if (event & BEV_EVENT_ERROR)
    {
        work_thread_->Disconnect(this);
    }
}

void SocketManager::ReadFromNetBus(Metadata *meta)
{
    // 解析消息头
    MsgHeader header;
    header.ParseFromArray(meta->GetDataHeader(), meta->GetDataHeaderSize());
    // 只接受发送给自己的消息或广播消息
    if (header.to() == this->user_id_ || (header.to() == 0
#ifndef DEBUG
                                          && header.from() != this->user_id_
#endif
                                          ))
    {
        header.set_to(this->user_id_);
        this->SendMessage(header.SerializeAsString().c_str(), header.ByteSizeLong(), meta->GetData(), meta->GetDataSize());
        // 如果是消息发送请求的类型则在向客户端推送完成后向发送端返回一条消息确认作为回执
        // 确认回执会被重新发送到总线上由其他连接负责读取
        if (header.msg_type() == MsgType::MSG_SEND_REQ)
        {
            // reply sender
            MsgHeader resp_header;
            resp_header.set_to(header.from());
            resp_header.set_from(0);
            resp_header.set_msg_type(MsgType::MSG_RECV_RESP);
            resp_header.set_msg_size(0);
            resp_header.set_msg_id(header.msg_id());
            work_thread_->SendToNetBus(resp_header.SerializeAsString().c_str(), resp_header.ByteSizeLong(), NULL, 0);
        }
    }
}

bool SocketManager::SendMessage(const char *header, int header_size, const char *data, int data_size)
{
    int ret = bufferevent_write(buff_ev_, PKT_FLAG, 4);
    if (ret == -1)
    {
        return false;
    }
    ret = bufferevent_write(buff_ev_, &header_size, 4);
    if (ret == -1)
    {
        return false;
    }
    ret = bufferevent_write(buff_ev_, header, header_size);
    if (ret == -1)
    {
        return false;
    }
    if (data_size > 0)
    {
        ret = bufferevent_write(buff_ev_, data, data_size);
        if (ret == -1)
        {
            return false;
        }
    }
    return true;
}

void SocketManager::RecvConnectReqest(sec::MsgHeader *header, const char *data, int data_size)
{
    ConnectReq connect_req;
    connect_req.ParseFromArray(data, data_size);
    long ts = GetTimestamp();
    ConnectResp connResp;
    connResp.set_timestamp(ts);
    if (connect_req.is_need_key())
    {
        connResp.set_pub_key(AppContext::Get()->server_pub_key());
    }
    MsgHeader msgHeader;
    msgHeader.set_msg_size(connResp.ByteSizeLong());
    msgHeader.set_msg_type(MsgType::CONNECT_RESP);
    msgHeader.set_from(0);
    msgHeader.set_to(0);
    this->SendMessage(msgHeader.SerializeAsString().c_str(), msgHeader.ByteSizeLong(), connResp.SerializeAsString().c_str(), connResp.ByteSizeLong());
}

void SocketManager::RecvLoginRequest(MsgHeader *header, const char *data, int data_size)
{
    CryptoGraphic cg;
    // 将密文用服务端私钥解密成明文
    string plain_text = cg.DecryptRSA(AppContext::Get()->server_prv_key(), data, data_size);
    if (!plain_text.empty())
    {
        LoginResp login_resp;
        LoginReq loginReq;
        loginReq.ParseFromString(plain_text);
        // 用户名和密码鉴权
        Certification c;
        if (c.Verify(loginReq.username().c_str(), loginReq.password().c_str()))
        {
            LOG4CPLUS_DEBUG(SimpleLogger::Get()->LoggerRef(), "客户端登录验证通过");
            FileUtils futils;
            stringstream ss;
            ss << ".shadow/" << loginReq.username() << "/";
            futils.MDir(ss.str().c_str());
            ss << "pub.pem";
            string pub_filename = ss.str();
            bool is_pub = futils.Exist(pub_filename.c_str());
            ss.clear();
            ss.str("");
            ss << ".shadow/" << loginReq.username() << "/"
               << "prv.pem";
            string prv_filename = ss.str();
            bool is_prv = futils.Exist(prv_filename.c_str());
            // 判断当前登录用户是否生成了密钥对，如果没有生成在表示首次登录，服务端在目录下生成通讯秘钥对
            // 私钥将发送给用户，公钥负责分发
            if (!is_pub || !is_prv)
            {
                LOG4CPLUS_INFO(SimpleLogger::Get()->LoggerRef(), "生成当前用户公私钥");
                CryptoGraphic cg;
                cg.GenerateRSA(RSA_LENGTH);
                futils.FWrite(pub_filename.c_str(), cg.PublicKey().c_str(), cg.PublicKey().size());
                futils.FWrite(prv_filename.c_str(), cg.PrivateKey().c_str(), cg.PrivateKey().size());
            }

            // 从别名表中获取当前登录用户的别名
            username_ = loginReq.username();
            alias_ = AppContext::Get()->GetAndReloadAlias(loginReq.username());

            Utils utils;
            // 判断当前用户是否已经登录，不允许重复登录
            if (!AppContext::Get()->UserOnLine(utils.StringHashCode(username_.c_str()), alias_))
            {
                login_resp.set_resp(LoginResp_LoginRespType::LoginResp_LoginRespType_NOUSER);
                header->set_msg_size(login_resp.ByteSizeLong());
                header->set_msg_type(MsgType::CLIENT_LOGIN_RESP);
                header->set_to(header->from());
                header->set_from(0);
                this->SendMessage(header->SerializeAsString().c_str(), header->ByteSizeLong(), login_resp.SerializeAsString().c_str(), login_resp.ByteSizeLong());
                return;
            }
            // 创建登录应答
            login_resp.set_resp(LoginResp_LoginRespType::LoginResp_LoginRespType_OK);
            // 生成随机token
            this->token_ = utils.GenRandToken();
            login_resp.set_token(token_);
            this->user_id_ = utils.StringHashCode(username_.c_str());
            login_resp.set_id(user_id_);
            login_resp.set_alias(alias_);
            // 如果用户登录请求中请求了私钥，则将私钥文件也一起发送出去
            if (loginReq.is_need_key())
            {
                login_resp.set_prv_key(futils.FRead(prv_filename.c_str()));
            }
            // 当前用户登录成功，想消息总线上发送一条广播，通知所有在线客户端新用户登录
            UserStatus user_status;
            UserStatus_State *state = user_status.add_status_list();
            state->set_alias(alias_);
            state->set_user_id(this->user_id_);
            state->set_is_on_line(true);
            MsgHeader status_header;
            status_header.set_msg_type(MsgType::USER_STATUS_NOTICE);
            status_header.set_from(this->user_id_);
            status_header.set_to(0);
            status_header.set_msg_size(user_status.ByteSizeLong());
            work_thread_->SendToNetBus(status_header.SerializeAsString().c_str(),
                                       status_header.ByteSizeLong(),
                                       user_status.SerializeAsString().c_str(),
                                       user_status.ByteSizeLong());
        }
        else
        {
            // 用户名密码验证失败
            LOG4CPLUS_INFO(SimpleLogger::Get()->LoggerRef(), "用户登录验证失败.");
            login_resp.set_resp(LoginResp_LoginRespType::LoginResp_LoginRespType_NOUSER);
        }
        // 发送登录响应
        header->set_msg_size(login_resp.ByteSizeLong());
        header->set_msg_type(MsgType::CLIENT_LOGIN_RESP);
        header->set_to(header->from());
        header->set_from(0);
        this->SendMessage(header->SerializeAsString().c_str(), header->ByteSizeLong(), login_resp.SerializeAsString().c_str(), login_resp.ByteSizeLong());
    }
}

void SocketManager::RecvHeartBeat(const char *data, int data_size)
{
    HeartBeat heartBeat;
    heartBeat.ParseFromArray(data, data_size);
}

void SocketManager::RecvUserInfoRequest(MsgHeader *header, const char *data, int data_size)
{
    UserInfoReq user_info_req;
    bool ret = user_info_req.ParseFromArray(data, data_size); // 解析用户消息请求
    if (!ret)
    {
        return;
    }
    string username = AppContext::Get()->GetUsername(user_info_req.alias());
    // 根据用户名生成散列值
    Utils utils;
    unsigned int user_id = utils.StringHashCode(username.c_str());
    UserInfoResp user_info_resp;
    user_info_resp.set_alias(user_info_req.alias());
    user_info_resp.set_user_id(user_id);
    // 判断请求是否包含需要用户的通信公钥
    if (user_info_req.is_need_key())
    {
        stringstream ss;
        ss << "需要读取用户私钥:" << username;
        LOG4CPLUS_DEBUG(SimpleLogger::Get()->LoggerRef(), ss.str());
        FileUtils futils;
        string path = ".shadow/" + username + "/pub.pem";
        string pub_text = futils.FRead(path.c_str());
        user_info_resp.set_pub_key(pub_text.c_str());
    }
    header->set_msg_size(user_info_resp.ByteSizeLong());
    header->set_msg_type(MsgType::USER_INFO_RESP);
    header->set_to(header->from());
    header->set_from(0);
    this->SendMessage(header->SerializeAsString().c_str(), header->ByteSizeLong(), user_info_resp.SerializeAsString().c_str(), user_info_resp.ByteSizeLong());
}

void SocketManager::RecvUserStatusRequest(MsgHeader *header, const char *data, int data_size)
{
    // 获取当前在线用户列表，遍历组装数据并发送到客户端
    std::map<unsigned int, std::string> online_table = AppContext::Get()->online_table();
    UserStatus user_status;
    for (auto it = online_table.begin(); it != online_table.end(); it++)
    {
#ifndef DEBUG
        if (it->first == this->user_id_)
        {
            continue;
        }
#endif
        UserStatus_State *state = user_status.add_status_list();
        state->set_is_on_line(true);
        state->set_user_id(it->first);
        state->set_alias(it->second);
    }
    header->set_to(this->user_id_);
    header->set_from(0);
    header->set_msg_type(MsgType::USER_STATUS_RESP);
    header->set_msg_size(user_status.ByteSizeLong());
    this->SendMessage(header->SerializeAsString().c_str(), header->ByteSizeLong(), user_status.SerializeAsString().c_str(), user_status.ByteSizeLong());
}

// 来自其他线程/进程的服务注册接口
void SocketManager::RecvServiceRegist(sec::MsgHeader *header, const char *data, int data_size)
{
    ServiceRegist service_regist;
    service_regist.ParseFromArray(data, data_size);
    this->username_ = service_regist.service_name();
    Utils utils;
    this->user_id_ = utils.StringHashCode(username_.c_str());
    this->alias_ = service_regist.service_name();
    AppContext::Get()->RegistService(service_regist.service_name());
}

time_t SocketManager::GetTimestamp()
{
    chrono::time_point<chrono::system_clock, std::chrono::milliseconds> tp = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
    return tp.time_since_epoch().count();
}